using System;
using System.Collections;
using System.Text;

using Qiang.Topology.Geometries;
using Qiang.Topology.Algorithm;

namespace Qiang.Topology.Noding
{
    /// <summary>
    /// Computes the intersections between two line segments in {SegmentString}s
    /// and adds them to each string.
    /// The {SegmentIntersector} is passed to a {Noder}.
    /// The {addIntersections} method is called whenever the {Noder}
    /// detects that two SegmentStrings <i>might</i> intersect.
    /// This class is an example of the <i>Strategy</i> pattern.
    /// </summary>
    public class SegmentIntersector
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="i1"></param>
        /// <param name="i2"></param>
        /// <returns></returns>
        public static bool IsAdjacentSegments(int i1, int i2)
        {
            return Math.Abs(i1 - i2) == 1;
        }

        /**
         * These variables keep track of what types of intersections were
         * found during ALL edges that have been intersected.
         */
        private bool hasIntersection = false;
        private bool hasProper = false;
        private bool hasProperInterior = false;
        private bool hasInterior = false;

        // the proper intersection point found
        private Coordinate properIntersectionPoint = null;

        private LineIntersector li;
        private bool recordIsolated = false;     
   
        /// <summary>
        /// 
        /// </summary>
        public int numIntersections = 0;

        /// <summary>
        /// 
        /// </summary>
        public int numInteriorIntersections = 0;

        /// <summary>
        /// 
        /// </summary>
        public int numProperIntersections = 0;

        /// <summary>
        /// Testion only
        /// </summary>
        public int numTests = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="li"></param>
        public SegmentIntersector(LineIntersector li)
        {
            this.li = li;
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual LineIntersector LineIntersector
        {
            get
            {
                return li;
            }
        }

        /// <summary>
        /// Returns the proper intersection point, or <c>null</c> if none was found
        /// </summary>
        public virtual Coordinate ProperIntersectionPoint
        {
            get
            {
                return properIntersectionPoint;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual bool HasIntersection
        {
            get
            {
                return hasIntersection;
            }
        }

        /// <summary> 
        /// A proper intersection is an intersection which is interior to at least two
        /// line segments.  Note that a proper intersection is not necessarily
        /// in the interior of the entire Geometry, since another edge may have
        /// an endpoint equal to the intersection, which according to SFS semantics
        /// can result in the point being on the Boundary of the Geometry.
        /// </summary>
        public virtual bool HasProperIntersection
        {
            get
            {
                return hasProper;
            }
        }

        /// <summary>
        /// A proper interior intersection is a proper intersection which is not
        /// contained in the set of boundary nodes set for this SegmentIntersector.
        /// </summary>
        public virtual bool HasProperInteriorIntersection
        {
            get
            {
                return hasProperInterior;
            }
        }

        /// <summary> 
        /// An interior intersection is an intersection which is
        /// in the interior of some segment.
        /// </summary>
        public virtual bool HasInteriorIntersection
        {
            get
            {
                return hasInterior;
            }
        }

        /// <summary>
        /// A trivial intersection is an apparent self-intersection which in fact
        /// is simply the point shared by adjacent line segments.
        /// Note that closed edges require a special check for the point shared by the beginning
        /// and end segments.
        /// </summary>
        private bool IsTrivialIntersection( SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
        {
            if (e0 == e1)
            {
                if (li.IntersectionNum == 1)
                {
                    if (IsAdjacentSegments(segIndex0, segIndex1))
                        return true;
                    if (e0.IsClosed)
                    {
                        int maxSegIndex = e0.Count - 1;
                        if ((segIndex0 == 0 && segIndex1 == maxSegIndex) || (segIndex1 == 0 && segIndex0 == maxSegIndex))
                            return true;
                    }
                }
            }
            return false;
        }

        /// <summary> 
        /// This method is called by clients
        /// of the {SegmentIntersector} class to process
        /// intersections for two segments of the {SegmentStrings} being intersected.
        /// Note that some clients (such as <c>MonotoneChain</c>s) may optimize away
        /// this call for segment pairs which they have determined do not intersect
        /// (e.g. by an disjoint envelope test).
        /// </summary>
        public virtual void ProcessIntersections(SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
        {
            if (e0 == e1 && segIndex0 == segIndex1) 
                return;
            numTests++;
            Coordinate p00 = e0.Coordinates[segIndex0];
            Coordinate p01 = e0.Coordinates[segIndex0 + 1];
            Coordinate p10 = e1.Coordinates[segIndex1];
            Coordinate p11 = e1.Coordinates[segIndex1 + 1];            

            li.ComputeIntersection(p00, p01, p10, p11);            
            if (li.HasIntersection)
            {
                if (recordIsolated)
                {
                    e0.Isolated = false;
                    e1.Isolated = false;
                }
                numIntersections++;                
                if (li.IsInteriorIntersection())
                {
                    numInteriorIntersections++;
                    hasInterior = true;
                }
                // if the segments are adjacent they have at least one trivial intersection,
                // the shared endpoint.  Don't bother adding it if it is the
                // only intersection.
                if (!IsTrivialIntersection(e0, segIndex0, e1, segIndex1))
                {
                    hasIntersection = true;
                    e0.AddIntersections(li, segIndex0, 0);
                    e1.AddIntersections(li, segIndex1, 1);
                    if (li.IsProper)
                    {
                        numProperIntersections++;
                        hasProper = true;
                        hasProperInterior = true;
                    }
                }
            }
        }
    }
}
